package com.isp.aut.server.dubbo;

import com.isp.aut.api.client.IAuthClient;
import com.isp.aut.server.service.GroupService;
import com.isp.aut.server.service.PermissionService;
import com.isp.aut.server.service.ResourceService;
import com.isp.common.model.user.GroupDTO;
import com.isp.common.model.user.PermissionDTO;
import com.isp.common.model.vo.Result;
import com.isp.org.api.dto.Resource;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.AntPathMatcher;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@DubboService
@Slf4j
@RequiredArgsConstructor
public class AuthClientImpl implements IAuthClient {
    /**
     * Authorization认证开头是"bearer "
     */
    private static final String BEARER = "Bearer ";

    @Autowired
    private PermissionService permissionService;

    /**
     * jwt token 密钥，主要用于token解析，签名验证
     */
    @Value("${spring.security.oauth2.jwt.signingKey}")
    private String signingKey;

    /**
     * 不需要网关签权的url配置(/oauth,/open)
     * 默认/oauth开头是不需要的
     */
    @Value("${gate.ignore.authentication.startWith}")
    private String ignoreUrls = "/oauth";

    private final ResourceService resourceService;
    private final GroupService groupService;
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Result<Boolean> dataAuthenticate(String authentication, String groupCode, PermissionDTO permissionDTO) {
        Jws<Claims> claimsJws = getJwt(authentication);
        String username = claimsJws.getPayload().get("username", String.class);
        List<GroupDTO> groupList = groupService.queryGroupsByUsername(username);
        //获取组权限列表
        List<PermissionDTO> groupPermissions = groupList.stream()
                .flatMap(x -> permissionService.queryPermissionsByGroupCode(permissionDTO).stream())
                .collect(Collectors.toList());
        return Result.success(isContainsPermission(groupPermissions, permissionDTO));
    }

    private Boolean isContainsPermission(List<PermissionDTO> groupPermissions, PermissionDTO requiredPermission) {
        //权限的父级继承
        for (PermissionDTO groupPermission : groupPermissions) {
            if(requiredPermission.getResFullPath().startsWith(groupPermission.getResFullPath())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Result<Boolean> authenticate(String authentications, String url, String method) {
        Jws<Claims> claimsJws = getJwt(authentications);
        String username = claimsJws.getPayload().get("username", String.class);
        Set<Resource> resources = resourceService.queryByUsername(username);
        return Result.success(isMatch(resources, url, method));
    }

    private Boolean isMatch(Set<Resource> resources, String url, String method) {
        for (Resource menu : resources) {
            if (StringUtils.isNotEmpty(menu.getUrl()) && antPathMatcher.match(menu.getUrl(), url)) {
                if (StringUtils.isNotEmpty(menu.getMethod())) {
                    if (method.equalsIgnoreCase(menu.getMethod())) {
                        return true;
                    }
                } else {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public boolean ignoreAuthentication(String url) {
        return Stream.of(this.ignoreUrls.split(",")).anyMatch(ignoreUrl -> url.startsWith(StringUtils.trim(ignoreUrl)));
    }

    @Override
    public boolean hasPermission(Result authResult) {
        log.debug("签权结果:{}", authResult.getData());
        return authResult.isSuccess() && (boolean) authResult.getData();
    }

    @Override
    public boolean hasPermission(String authentication, String url, String method) {
        // 如果请求未携带token信息, 直接权限
        if (StringUtils.isBlank(authentication) || !authentication.startsWith(BEARER)) {
            log.error("user token is null");
            return Boolean.FALSE;
        }
        //token是否有效，在网关进行校验，无效/过期等
        if (invalidJwtAccessToken(authentication)) {
            return Boolean.FALSE;
        }
        //从认证服务获取是否有权限,远程调用
        return hasPermission(authenticate(authentication, url, method));
    }

    @Override
    public Jws<Claims> getJwt(String jwtToken) {
        if (jwtToken.startsWith(BEARER)) {
            jwtToken = StringUtils.substring(jwtToken, BEARER.length());
        }
        return Jwts.parser()  //得到DefaultJwtParser
                .setSigningKey(signingKey.getBytes()).build() //设置签名的秘钥
                .parseClaimsJws(jwtToken);
    }

    @Override
    public boolean invalidJwtAccessToken(String authentication) {
        // 是否无效true表示无效
        boolean invalid = Boolean.TRUE;
        try {
            getJwt(authentication);
            invalid = Boolean.FALSE;
        } catch (ExpiredJwtException | MalformedJwtException ex) {
            log.error("user token error :{}", ex.getMessage());
        }
        return invalid;
    }
}
